#include "protocol.h"
#include "server.h"

/* 创建一个监听套接字 */
int retListen(char *MYIP, char *MYPORT)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket()");
		exit(1);
	}
	
	/* 设置同一地址可复用*/
	int val = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(val));

	struct sockaddr_in myaddr;
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(atoi(MYPORT));
	inet_pton(AF_INET, MYIP, &myaddr.sin_addr.s_addr);
	/* 绑定ip和端口号 */
	if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0)
	{
		perror("bind()");
		exit(1);
	}
	
	/* 监听 */
	if (listen(sockfd, 10) < 0)
	{
		perror("listen()");
		exit(1);
	}
	return sockfd;
}

static int unpack(struct FSM_ST *fsm)
{
	u_int8_t ch;
	u_int8_t destOpt[BUFFSIZE] = {0};
	int i = 0;
	
	/* 读取到包头 */
	do
	{
		ch = fsm->recvbuff[i++];
	
	}while (ch != 0xC0);
	
	/* 为防止读到错误的包头*/
	while (ch == 0xC0)
	{
		ch = fsm->recvbuff[i++];
	}

	int k = 0;
	
	/* 解析真正内容 @pkg_len:内容长度 @cmd_no:请求命令*/
	while (ch != 0xC0)
	{
		destOpt[k++] = ch;
		ch = fsm->recvbuff[i++];
	}
	int pkg_len = destOpt[0] | destOpt[1] << 8 | destOpt[2] << 16 | destOpt[3] << 24;

	if (pkg_len != k)
	{
		return -1;
	}
	fsm->cmd_no = destOpt[4] | destOpt[5] << 8 | destOpt[6] << 16 | destOpt[7] << 24;

	if (fsm->cmd_no == FTP_CMD_GET)
    {
	    int arg_len_1 = destOpt[8] | destOpt[9] << 8 | destOpt[10] << 16 | destOpt[11] << 24;
        int j;
	    for(j = 0; j < arg_len_1; j++)
        {
            fsm->filename[j] = destOpt[12+j];
        }
	    fsm->filename[j] = '\0';
	    puts(fsm->filename);
    }
	return 0;
}

/* pkg_len cmd_no resp_len result filenames  */
static void packet(char *srcbuff, char *destbuff)
{
	int namelen = strlen(srcbuff);
	
	int pkg_len = 4 + 4 + 4 + 1 + namelen;
	enum CMD_NO cmd_no = FTP_CMD_LS;
	int resp_len = namelen + 1;
	
	int i = 0;
	destbuff[i++] = 0xC0;

	destbuff[i++] = pkg_len & 0xff;
	destbuff[i++] = (pkg_len >> 8) & 0xff;
	destbuff[i++] = (pkg_len >> 16) & 0xff;
	destbuff[i++] = (pkg_len >> 24) & 0xff;

	destbuff[i++] = cmd_no & 0xff;
	destbuff[i++] = (cmd_no >> 8) & 0xff;
	destbuff[i++] = (cmd_no >> 16) & 0xff;
	destbuff[i++] = (cmd_no >> 24) & 0xff;

	destbuff[i++] = resp_len & 0xff;
	destbuff[i++] = (resp_len >> 8) & 0xff;
	destbuff[i++] = (resp_len >> 16) & 0xff;
	destbuff[i++] = (resp_len >> 24) & 0xff;
	
	/* 结果 */
	destbuff[i++] = 0;
	
	while (*srcbuff)
	{
		destbuff[i++] = *srcbuff;
		srcbuff++;
	}

	destbuff[i++] = 0xC0;
	destbuff[i] = '\0';

}

static void execLs(int confd)
{
	DIR *dir = opendir(SERVERPATH);	
	if (dir == NULL)
	{
		perror("opendir()");
		exit(1);
	}
	
	struct dirent *dirp;
	int pos = 0;
	u_int8_t filename[NAMESIZE] = {0};
	u_int8_t destname[NAMESIZE] = {0};
	while(dirp = readdir(dir))
	{
		if ((strcmp(dirp->d_name, ".") != 0) && (strcmp(dirp->d_name, "..") != 0))
		{
			pos += sprintf(filename+pos, "%s ", dirp->d_name);
		}
	}
	packet(filename, destname);
	send(confd, destname, BUFFSIZE, 0);
	closedir(dir);
}

static void execGet(struct FSM_ST *fsm)
{
    struct stat statbuf;
    char realname[NAMESIZE] = {0};
    char destbuff[BUFFSIZE] = {0};
    int ret;

    sprintf(realname, "%s/%s", SERVERPATH, fsm->filename);
    if ((ret = lstat(realname, &statbuf)) < 0)
    {
        perror("lstat()");
        exit(1);
    }

    int pkg_len = 4 + 4 + 4 + 1 + 4 + strlen(fsm->filename);
    enum CMD_NO cmd_no = FTP_CMD_GET;
    int resp_len = 1 + 4;
    int filesize = statbuf.st_size;

    int i = 0;
    destbuff[i++] = 0xC0;

    destbuff[i++] = pkg_len & 0xff;
    destbuff[i++] = (pkg_len >> 8) & 0xff;
    destbuff[i++] = (pkg_len >> 16) & 0xff;
    destbuff[i++] = (pkg_len >> 24) & 0xff;

    destbuff[i++] = cmd_no & 0xff;
    destbuff[i++] = (cmd_no >> 8) & 0xff;
    destbuff[i++] = (cmd_no >> 16) & 0xff;
    destbuff[i++] = (cmd_no >> 24) & 0xff;

    destbuff[i++] = resp_len & 0xff;
    destbuff[i++] = (resp_len >> 8) & 0xff;
    destbuff[i++] = (resp_len >> 16) & 0xff;
    destbuff[i++] = (resp_len >> 24) & 0xff;

    /* 结果 */
    destbuff[i++] = ret < 0 ? 1 : 0;

    destbuff[i++] = filesize & 0xff;
    destbuff[i++] = (filesize >> 8) & 0xff;
    destbuff[i++] = (filesize >> 16) & 0xff;
    destbuff[i++] = (filesize >> 24) & 0xff;

    char *p = fsm->filename;
    while (*p)
    {
        destbuff[i++] = *p;
        p++;
    }
    destbuff[i++] = 0xC0;
    destbuff[i] = '\0';

    send(fsm->confd, destbuff, BUFFSIZE, 0);

    int fd;
    fd = open(realname, O_RDONLY);
    if (fd < 0)
    {
        perror("open()");
        exit(1);
    }

    unsigned char readbuf[BUFFSIZE]={0};
    int r;
    while(1)
    {
        r = read(fd, readbuf , BUFFSIZE);
        if(r > 0)
        {
            send(fsm->confd, readbuf, r, 0);
        }
        else if(r == 0)
        {
            break;
        }
        else
        {
            perror("read()");
            exit(1);
        }
        memset(readbuf, 0, BUFFSIZE);

    }
    close(fd);
}

static void handlerConnection(struct FSM_ST *fsm)
{
	switch (fsm->cmd_no)
	{
        case FTP_CMD_LS:
            execLs(fsm->confd);
            break;
        case FTP_CMD_GET:
            execGet(fsm);
            break;
        case FTP_CMD_PUT:
            break;
        case FTP_CMD_BYE:
            close(fsm->confd);
            exit(0);
            break;
        default:
            break;
    }
}

static void driver(struct FSM_ST *fsm)
{

    switch (fsm->state)
    {
        case FSM_RECV:
            if (recv(fsm->confd, fsm->recvbuff, BUFFSIZE, 0) < 0)
            {
                fsm->errstr = "recv()";
                fsm->state = FSM_EX;
            }
            else
                fsm->state = FSM_UNPC;
            break;
        case FSM_UNPC:
            if (unpack(fsm) < 0)
            {
                fsm->errstr = "unpack()";
                fsm->state = FSM_EX;
            }
            else
                fsm->state = FSM_REPLY;
            break;
        case FSM_REPLY:
            handlerConnection(fsm);
            fsm->state = FSM_RECV;
            memset(fsm->recvbuff, 0, BUFFSIZE);
            memset(fsm->filename, 0, BUFFSIZE);
           break;
        case FSM_EX:
            perror(fsm->errstr);
            fsm->state = FSM_T;
            break;
        case FSM_T:
            exit(1);
            break;
        default:
            abort();
            break;
    }
}

int main(int argc, char **argv)
{
	if (argc < 3)
	{
		fprintf(stderr, "Usage:\n");
		exit(1);
	}

	int sockfd = retListen(argv[1], argv[2]);

	while (1)
	{
		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		/* 接收一个连接并保存客户端的信息在结构体中 */
		int confd;
		confd = accept(sockfd, (struct sockaddr *)&client, &len);
		if (confd < 0)
		{
			perror("accept()");
			exit(1);
		}
		
		pid_t pid;
		int retlen;
		/* 创建一个子进程完成每个连接的任务 */
		pid = fork();
		if (pid < 0)
		{
			perror("fork()");
			exit(1);
		}
		
		if (pid == 0)
		{
			close(sockfd);
			struct FSM_ST fsm;
			fsm.state = FSM_RECV;
			fsm.confd = confd;
            memset(fsm.filename, 0, BUFFSIZE);
            memset(fsm.recvbuff, 0, BUFFSIZE);
			while (fsm.state != FSM_T)
			{
				driver(&fsm);
			}
			close(confd);
			exit(0);
		}
		close(confd);
	}

	close(sockfd);
	exit(0);
}
